package com.aos.sysService.api;


import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.xframe.common.core.api.GenericityController;
import com.xframe.common.core.constant.ErrorConstant;
import com.xframe.common.core.dto.ErrorCode;
import com.xframe.common.core.dto.QueryDTO;
import com.xframe.common.core.dto.ResultDTO;
import com.xframe.common.core.exception.BusinessException;
import com.xframe.common.core.service.UserCredentials;
import com.xframe.domain.CommandModel;
import com.xframe.domain.CredentialsContainer;
import com.xframe.domain.ModelRepositoryFactory;
import com.xframe.domain.knowledgeBase.*;
import com.xframe.domain.repository.SysBaseRepository;
import com.xframe.domain.role.IUserCredentials;
import com.xframe.domain.sys.SysModel;
import com.xframe.xdal.core.DataBaseAccess;
import com.xframe.xdal.core.domain.SysBaseEntity;
import com.xframe.xdal.core.exception.DalFrameException;
import com.xframe.xdal.core.model.ConditionValue;
import com.xframe.xdal.core.model.DbSort;
import com.xframe.xdal.core.model.FindOptions;
import com.xframe.xdal.core.model.mapping.TableInfoBuilder;
import com.xframe.xdal.core.model.page.IPagerModel;
import com.xframe.xdal.core.serialize.IJSONSer;
import com.xframe.xdal.core.serialize.JsonSerFactory;
import com.xframe.xdal.core.util.CollectionUtil;
import com.xframe.xdal.core.util.StringUtil;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.poi.hpsf.Decimal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.bind.annotation.*;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

import static com.xframe.domain.ModelErrorCode.*;


@Log4j2
@RestController
@RequiredArgsConstructor
@EnableAsync
@RequestMapping("/base")
@CrossOrigin(origins = "*")
public class MainFacade extends GenericityController{

    @Autowired
    private RestTemplate restTemplate;

    private static final String SERVICE_PROVIDER = "http://150.158.135.136:9001";
    // 地球半径，单位为公里
    private static final double EARTH_RADIUS = 6371.0;

    @Value("${project.env}")
    private String projectEnv;

    @GetMapping("env")
    @ApiOperation(value = "查询项目环境", notes = "查询项目环境")
    public String env() {
        return "当前环境为：" + projectEnv;
    }



    /**
     * 查询列表
     *
     * @param queryDTO 查询条件
     * @return
     */
    @PostMapping({"/queryVKnowledgeBaseListByPage"})
    @ResponseBody
    public ResultDTO<Object> queryVKnowledgeBaseListByPage(@RequestBody QueryDTO queryDTO) {
        try {
            IUserCredentials userCredentials = checkUserToken();

            FindOptions findOptions = FindOptions.defFindAllOptionsByPage();

            findOptions.setPageIndex(queryDTO.getPageIndex());
            findOptions.setPageSize(queryDTO.getPageSize());

            findOptions.setRecordCountFlag(queryDTO.getRecordCountFlag());
            if (queryDTO.getConditions() != null && queryDTO.getConditions().size() > 0) {
                for (int i = 0; i < queryDTO.getConditions().size(); i++) {
                    findOptions.getConditionValueCollection().add(queryDTO.getConditions().get(i).toConditionValue());
                }
            }

            if (!StringUtil.isEmpty(queryDTO.getSortName())) {
                if (StringUtil.isEmpty(queryDTO.getSortDir())) {
                    queryDTO.setSortDir("ASC");
                }
                findOptions.getDbSortCollection().add(new DbSort(queryDTO.getSortName(), DbSort.SortDir.valueOf(queryDTO.getSortDir().toUpperCase())));
            }

            if(queryDTO.getRefList() != null && !queryDTO.getRefList().isEmpty()) {
                findOptions.setRefList(queryDTO.getRefList());
            }

            if(StringUtil.isEmpty(queryDTO.getModelCode())){
                throw new BusinessException(ErrorCode.MODELID_NULL, ErrorConstant.MODELID_NULL_MSG);
            }

            SysModel sysModel = SysModel.getInstance(queryDTO.getModelCode());
            if (sysModel == null) {
                throw new BusinessException(ErrorCode.DB_RECORD_NOFIND,
                        StringUtil.format(ErrorConstant.DB_RECORD_NOFIND_MSG, queryDTO.getModelCode()));
            }
            SysBaseRepository repository = ModelRepositoryFactory.builder(sysModel);
            Class<?> dataType = VKnowledgeBase.class;

            IPagerModel<List<VKnowledgeBase>> collection = repository.queryModleByPage(dataType,sysModel,findOptions);
            //IJSONSer jsonSer = JsonSerFactory.create(sysModel);

            List<VKnowledgeBase> vKnowledgeBaseList = collection.getDataList();

            if(vKnowledgeBaseList != null && !vKnowledgeBaseList.isEmpty()){
                for(VKnowledgeBase vKnowledgeBase : vKnowledgeBaseList){
                    FindOptions findOptions1 = new FindOptions();
                    findOptions1.addConditionValue(new ConditionValue("knId",vKnowledgeBase.getKnId()));
                    List<KnowledgeFiles> knowledgeFilesList = DataBaseAccess.getInstance().findAll(KnowledgeFiles.class,findOptions1);
                    vKnowledgeBase.setKnowledgeFilesList(knowledgeFilesList);
                    if(vKnowledgeBase.getKnowledgeFilesList() != null && !vKnowledgeBase.getKnowledgeFilesList().isEmpty()){

                        for(KnowledgeFiles knowledgeFiles : vKnowledgeBase.getKnowledgeFilesList()) {
                            if (knowledgeFiles.getFileCategory() == 1) {
                                vKnowledgeBase.setFileCodeUrl(knowledgeFiles.getFileUrl());
                            }
                        }
                    }
                }
            }

            //Object dic = jsonSer.toJsonObj(collection.getDataList(),sysModel,userCredentials.getLanguage(),queryDTO.getQueryKey(),queryDTO.getVer());
            //collection.setDataList(dic);
            return new ResultDTO<>().setResult(collection);
        } catch (BusinessException ex) {
            return new ResultDTO<>().error(ex.getCode(), ex.getMessage());
        } catch (Exception ex) {
            ex.printStackTrace();
            return new ResultDTO<>().error(ErrorCode.INTERNAL_SERVER_ERROR,ex.getMessage());
        }
    }

    /**
     * 测试
     *
     * @return
     */
    @GetMapping({ "/test" })
    @ResponseBody
    public String test(){
        System.out.println("进入存入redis接口");


        FindOptions findOptions = new FindOptions();
        findOptions.addConditionValue(new ConditionValue("busTablesId",1L));
        findOptions.addConditionValue(new ConditionValue("busType",4));
        List<EmpBuisinessFile> empBuisinessFiles = DataBaseAccess.getInstance().findAll(EmpBuisinessFile.class,findOptions);

        EmpFile empFile = DataBaseAccess.getInstance().findByPk(EmpFile.class,"014fa9ea584ee8ed0efa092c1966e4df","ALL");
        return "OK";
    }

    /**
     * app定时调用接口向redis存入当前在线员工信息
     * @param infos
     * @return
     */
    @PostMapping({ "/workerToRedis" })
    @ResponseBody
    public int workerToRedis(@RequestBody CommandModel infos){
        System.out.println("进入存入redis接口");
        return 1;
    }

    /**
     * 算法计算自动派单
     * @param repairWorkOrder
     * @return
     */
    @PostMapping({ "/algorithm" })
    @ResponseBody
    public JSONObject algorithm(@RequestBody RepairWorkOrder repairWorkOrder){
        JSONObject jsonResult = new JSONObject();
        // 获取所有维修工分配的维修单的时间段
        String url = SERVICE_PROVIDER + "/repair/getRepairEmployee";
        IUserCredentials userCredentials = CredentialsContainer.getCredentials();
        String token = userCredentials.getToken();

        if(token==null){
            jsonResult.set("code",USER_TOKEN_NULL_ERROR);
            jsonResult.set("msg",USER_TOKEN_NULL_ERROR_TEXT);
            return jsonResult;
//            throw new DalFrameException(USER_TOKEN_NULL_ERROR,USER_TOKEN_NULL_ERROR_TEXT);
        }

        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", token);
        String reqBody = "{\"pageNum\":1,\"pageSize\":999,\"day\":3}";
        HttpEntity<String> entity = new HttpEntity<>(reqBody,headers);
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
        String responseBody = response.getBody();
        // 无法获取所有维修工分配的维修单的时间段
        if(responseBody==null){
            jsonResult.set("code",WORKER_TIME_ERROR);
            jsonResult.set("msg",WORKER_TIME_ERROR_TEXT);
            return jsonResult;
//            throw new DalFrameException(WORKER_TIME_ERROR,WORKER_TIME_ERROR_TEXT);
        }
        JSONObject res = new JSONObject(responseBody);
        JSONObject info = res.getJSONObject("info");
        if(info==null){
            jsonResult.set("code",WORKER_TIME_ERROR);
            jsonResult.set("msg",WORKER_TIME_ERROR_TEXT);
            return jsonResult;
//            throw new DalFrameException(WORKER_TIME_ERROR,WORKER_TIME_ERROR_TEXT);
        }
        JSONArray rows = info.getJSONArray("rows");
        if(rows==null){
            jsonResult.set("code",WORKER_TIME_ERROR);
            jsonResult.set("msg",WORKER_TIME_ERROR_TEXT);
            return jsonResult;
//            throw new DalFrameException(WORKER_TIME_ERROR,WORKER_TIME_ERROR_TEXT);
        }

        // 读取redis内所有在线员工数据及其物理位置
        JSONArray redisEmpRows = getRedisWorker();
        JSONArray freeWorkers = new JSONArray();
        // 计算出当前处于空闲的维修工有哪些，作为可分配维修工
        for(int i=0;i<rows.size();i++){
            JSONObject workerRow = rows.getJSONObject(i);
            JSONArray workList = workerRow.getJSONArray("workList");
            if(workList.isEmpty()){
                workerRow.set("free",true);
                freeWorkers.put(workerRow);
            }else{
                workerRow.set("free",false);
            }
        }
        // 若没有空闲维修工则不分配
        if(freeWorkers.isEmpty()){
            jsonResult.set("msg","没有空闲的维修工，无法自动分配");
            return jsonResult;
        }
//        获取设备的经纬度
//        SysModel sysModel = SysModel.getInstance("com.xframe.domain.knowledgeBase.EquipmentInfo");
        FindOptions findOptions = FindOptions.defFindAllOptionsByPage();
        findOptions.getRefList().add(SysBaseEntity.FULL_DATA);

        SysModel sysModel = SysModel.getInstance(DataBaseAccess.getInstance(), EquipmentInfo.class.getName());
        if(sysModel == null){
            // 抛出异常
            throw new RuntimeException("sysModel为空,请检查配置文件");
        }
        SysBaseRepository sysBaseRepository = ModelRepositoryFactory.builder(sysModel);
//        WareHouse.class,allocatio.getDesWareHouseId(),

        //EquipmentInfo equipmentInfo = sysBaseRepository.findByPK(EquipmentInfo.class,repairWorkOrder.getEmId(),TableInfoBuilder.BuildTableInfoByAnnotation(EquipmentInfo.class));

        EquipmentInfo equipmentInfo = DataBaseAccess.getInstance().findByPk(EquipmentInfo.class,repairWorkOrder.getEmId());
        System.out.println(equipmentInfo);
        EquipmentTree equipmentTree = DataBaseAccess.getInstance().findByPk(EquipmentTree.class,equipmentInfo.getEmTree());

        if(equipmentInfo==null){
            jsonResult.set("code",EQUIPMENTINFO_ERROR);
            jsonResult.set("msg",EQUIPMENTINFO_ERROR_TEXT);
            return jsonResult;
//            throw new DalFrameException(EQUIPMENTINFO_ERROR,EQUIPMENTINFO_ERROR_TEXT);
        }
//        EquipmentTree equipmentTree = equipmentInfo.getRefEmTree();
        if(equipmentTree==null){
            jsonResult.set("code",EQUIPMENTTREE_ERROR);
            jsonResult.set("msg",EQUIPMENTTREE_ERROR_TEXT);
            return jsonResult;
//            throw new DalFrameException(EQUIPMENTTREE_ERROR,EQUIPMENTTREE_ERROR_TEXT);
        }
//        经度
        BigDecimal lat2 = equipmentTree.getPositionX();
//        纬度
        BigDecimal lon2 = equipmentTree.getPositionY();

        if(lat2==null||lon2==null){
            jsonResult.set("msg",POSITION_ERROR_TEXT);
            return jsonResult;
//            throw new DalFrameException(POSITION_ERROR,POSITION_ERROR_TEXT);
        }

        // 计算出可分配维修工与维修点之间的最短距离
        for(int i=0;i<freeWorkers.size();i++){
            JSONObject worker = freeWorkers.getJSONObject(i);
            JSONObject workerInfo = worker.getJSONObject("worker");
            for(int j=0;j<redisEmpRows.size();j++){
                JSONObject emp = redisEmpRows.getJSONObject(i);
//                空闲员工计算距离
                if(workerInfo.getStr("employeeId").equals(emp.getStr("employeeId"))){
                    double distance = calculateDistance(emp.getDouble("longitude"), emp.getDouble("latitude"), lat2.doubleValue(), lon2.doubleValue());
                    worker.set("distance",distance);
                }
            }
        }
        // 取最短距离最小值
        JSONObject minObject = null;
        Double minValue = Double.MAX_VALUE;

        for (int i = 0; i < freeWorkers.size(); i++) {
            JSONObject obj = freeWorkers.getJSONObject(i);
            System.out.println(obj);
            Double value = obj.getDouble("distance");
            if(value!=null){
                if (value < minValue) {
                    minValue = value;
                    minObject = obj;
                }
            }

        }

        if (minObject != null) {
            System.out.println("Minimum value: " + minValue + ", Object: " + minObject.toString());
        } else {
            System.out.println("No objects found.");
        }

        LocalDateTime now = LocalDateTime.now();

// 计算下一个半小时的分钟数（0或30）
        int nextHalfHourMinute = (now.getMinute() / 30 + 1) * 30 % 60;

// 如果现在是整点或已经过了半小时，则直接使用该分钟数
// 否则，需要调整为下一个半小时的分钟数
        LocalDateTime halfHourRounded = now.withMinute(nextHalfHourMinute).withSecond(0).withNano(0);

// 注意：上面的计算方式在绝大多数情况下都是正确的，
// 但如果现在是59分，并且你想要的是下一个小时的0分，
// 那么你可能需要稍微调整逻辑来确保它总是向上取整到下一个小时的开始
// 一种更健壮的方法是：
        int roundedHour = now.getHour();
        if (now.getMinute() >= 30) {
            roundedHour++; // 如果已经过了半小时，则小时数加1
            // 如果小时数增加到24，则需要回滚到0（但LocalDateTime会自动处理这个）
        }
// 然后设置小时和分钟为整点或半小时
        LocalDateTime robustHalfHourRounded = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), roundedHour,
                roundedHour % 2 == 0 ? 0 : 30, // 如果是偶数小时则设置为0分，否则为30分
                0, 0); // 秒和纳秒都设置为0
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

        // 使用formatter将halfHourRounded格式化为字符串
        String formattedDateTime = robustHalfHourRounded.format(formatter);
        minObject.set("startTime",formattedDateTime);
        return minObject;
    }

    public static JSONArray getRedisWorker(){


        JSONArray jsonArray = new JSONArray();
        for(int i=0;i<10;i++){
            JSONObject jsonObject = new JSONObject();
            int number = i+9;
            String str = String.format("%d", number);
            jsonObject.set("employeeId",str);
            //经度
            jsonObject.set("longitude",34.0522);
            //纬度
            jsonObject.set("latitude",-118.2437);

            jsonArray.add(jsonObject);
        }
        return jsonArray;
    }



    /**
     * 根据两点的经纬度计算两点之间的距离
     *
     * @param lat1 第一个点的纬度
     * @param lon1 第一个点的经度
     * @param lat2 第二个点的纬度
     * @param lon2 第二个点的经度
     * @return 两点之间的距离，单位为公里
     */
    public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        double dLat = Math.toRadians(lat2 - lat1);
        double dLon = Math.toRadians(lon2 - lon1);
        lat1 = Math.toRadians(lat1);
        lat2 = Math.toRadians(lat2);

        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
                + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return EARTH_RADIUS * c;
    }

}
